package com.wutianyi.images;

import java.awt.*;
import java.awt.event.*;
import java.awt.font.*;
import java.util.*;
import java.text.*;
import javax.swing.*;
import javax.swing.plaf.*;

// This is an example of how to use the text handling
// capabilities of Java2D to create attributed strings, that
// is, strings with embedded formatting information (see
// <AttributedString> and <TextLayout> class documentation).
// This panel displays the time in the upper left corner, and
// contains a timer instance that updates it once a second.
//--------------------------------------------------------------
// NOTE: this is not a really superb design because an instance
// of <AttributedString> and <TextLayout> are created once a second.
// Unfortunately, these are both immutable objects with respect to
// text content.

public class DigitalClockPanel extends JPanel
{
    // Constructor
    public DigitalClockPanel()
    {
        // Create the private UI for the component. The UI
        // is a delegate object that handles paint requests for
        // the component.
        this.setUI(DigitalClockPanelUI.createUI(this));

        // Make the panel opaque by default
        this.setOpaque(true);
    }

    // Test frame for the clock panel
    public static void main(String[] args)
    {
        // Create a frame and add the button
        JFrame frame = new JFrame();
        frame.setLocation(200, 200);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // Stick a clock panel in the frame, pack it and show it
        DigitalClockPanel panel = new DigitalClockPanel();
        frame.getContentPane().add(panel);
        frame.pack();
        frame.setVisible(true);
    }
}

// This UI delegate is extended from the basic panel UI that is
// the base class for all panel UI delegates in all PLAFs.
class DigitalClockPanelUI extends PanelUI implements ActionListener
{
    // Nice chunky clock font
    protected final static Font clockFont = new Font("Arial", Font.BOLD, 24);

    // Date formatter
    protected final static SimpleDateFormat dateFormat = new SimpleDateFormat("hh:mm:ss a");

    // Font rendering context - assumes no default transform,
    // anti-aliasing active and fractional metrics allowed.
    protected final static FontRenderContext frc = new FontRenderContext(null, true, true);

    // Ye old fudge factor to give a little space around the text
    protected final static int FUDGE = 6;

    // Attributed string containing current time
    protected AttributedString timeString = null;

    // Text layout of attributed string
    protected TextLayout textLayout = null;

    // One-second timer to keep clock current
    protected javax.swing.Timer timer = new javax.swing.Timer(1000, this);

    // Reference to panel that this delegate is for
    protected DigitalClockPanel panel;

    // Constructor - store the panel for which this delegate
    // was created
    private DigitalClockPanelUI(DigitalClockPanel panel)
    {
        // Store the panel and set the background color to black
        this.panel = panel;
        panel.setBackground(Color.black);

        // Artificially invoke the <ActionListener> to get things
        // rolling so there is a layout present when the component
        // is first painted, then start the timer.
        actionPerformed(null);
        timer.start();
    }

    // Create an instance of the UI delegate for this component
    public static ComponentUI createUI(JComponent component)
    {
        return new DigitalClockPanelUI((DigitalClockPanel) component);
    }

    // Paint the component - this version differs from the
    // one in <java.awt.Component> in that it has another
    // parameter specifying the particular component being
    // painted. This is what allows UI delegates (like this
    // one) to be shared among components.
    public void paint(Graphics g, JComponent c)
    {
        // If there are no time or layouts yet, we can't paint
        if (this.timeString == null || this.textLayout == null)
            return;

        // Get a reference to the <Graphics2D> instance and
        // set the anti-aliasing on
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        // Draw the string in the position dictated by
        // the layout.
        g2.drawString(timeString.getIterator(), 1, (int) (textLayout.getAscent()));

        // Get a reference to the <Graphics2D> instance and
        // set the anti-aliasing to the default (usually off)
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_DEFAULT);
    }

    // Invoked by timer when interval ends
    public void actionPerformed(ActionEvent event)
    {
        // Create a new attributed string with the new time
        timeString = new AttributedString(dateFormat.format(new Date()));

        // Set the whole string to be one font and one color
        timeString.addAttribute(TextAttribute.FONT, clockFont);
        timeString.addAttribute(TextAttribute.FOREGROUND, Color.red);

        // Pick out the colons (':') and make only them yellow. Notice
        // that the character ranges are inclusive for the lower
        // bound and exclusive for the upper.
        timeString.addAttribute(TextAttribute.FOREGROUND, Color.yellow, 2, 3);
        timeString.addAttribute(TextAttribute.FOREGROUND, Color.yellow, 5, 6);

        // Create a new text layout and signal the panel
        // that it needs repainting
        textLayout = new TextLayout(timeString.getIterator(), frc);
        panel.repaint();
    }

    // Return the preferred size for the component
    public Dimension getPreferredSize(JComponent c)
    {
        // Get the size dictated by the text layout as a
        // <Rectangle>, then get the <Dimension> from that.
        Dimension size = textLayout.getBounds().getBounds().getSize();

        // Add a little height and width to leave some
        // space around the text. We add '2' to leave one
        // extra pixel on the left and right so the text isn't
        // hitting the border when the frame is packed.
        size.height += FUDGE;
        size.width += (FUDGE + 2);
        return size;
    }
}
